/*
 * File:   MainGUI.cpp
 * Author: alrojas
 *
 * Created on 14 de junio de 2014, 8:26
 */

#include "MainGUI.h"

MainGUI::MainGUI(){
    
    setWindowTitle("Cutting Stock Optimization");
     
    centralWid = new QWidget(this);
    mainLay = new QGridLayout(centralWid);
    centralWid->setLayout(mainLay);    
    centralWid->setBackgroundRole(QPalette::Base);
    centralWid->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);
    
    createObjects();
    createActions();
    createMenus();
    
}

void MainGUI::createObjects(){
        
    /* Info Group  */
    infoGro = new QGroupBox("Info",centralWid);
    infoLay = new QFormLayout(infoGro);
    infoGro->setLayout(infoLay);
    
    nameLEdit = new QLineEdit(infoGro);
    nameLEdit->setEnabled(false);    
    sizeLEdit = new QLineEdit(infoGro); 
    sizeLEdit->setEnabled(false);
    idLEdit = new QLineEdit(infoGro); 
    idLEdit->setEnabled(false);
    
    infoLay->addRow("Name: ",nameLEdit);
    infoLay->addRow("Size: ",sizeLEdit);    
    infoLay->addRow("ID: ",idLEdit);
    
    /* Parameters Group */
    parametersGro = new QGroupBox("Parameters",centralWid);
    parametersLay = new QBoxLayout(QBoxLayout::LeftToRight,parametersGro);    
    parametersGro->setLayout(parametersLay);

    /* Eroding Sub-Group */
    erodingGro = new QGroupBox("Eroding",parametersGro);
    erodingLay = new QFormLayout(parametersGro);
    erodingGro->setLayout(erodingLay);

    eKernelSizeLEdit = new QSpinBox();
    eKernelSizeLEdit->setRange(1,10);
    eKernelSizeLEdit->setSingleStep(1);
    eKernelSizeLEdit->setValue(6);

    erodingLay->addRow("Kernel Size: ",eKernelSizeLEdit);

    /* Edge Detection Sub-Group */
    defectsDetectionGro = new QGroupBox("Defects Detection",parametersGro);
    defectsDetectionLay = new QFormLayout(parametersGro);
    defectsDetectionGro->setLayout(defectsDetectionLay);

    edThreshLEdit = new QSpinBox();
    edThreshLEdit->setRange(1,100);
    edThreshLEdit->setSingleStep(1);
    edThreshLEdit->setValue(81);

    edKernelSizeLEdit = new QSpinBox();
    edKernelSizeLEdit->setRange(1,100);
    edKernelSizeLEdit->setSingleStep(2);
    edKernelSizeLEdit->setValue(35);
        
    defectsDetectionLay->addRow("Thresh: ",edThreshLEdit);
    defectsDetectionLay->addRow("Kernel Size: ",edKernelSizeLEdit);
        
    parametersLay->addWidget(erodingGro);     
    parametersLay->addWidget(defectsDetectionGro);
    
    /* Results Group */        
    resultsGro = new QGroupBox("Results",centralWid);
    resultsLay = new QGridLayout(resultsGro);
    resultsGro->setLayout(resultsLay);
    
    defectsLEdit = new QLineEdit("0");
    defectsLEdit->setEnabled(false);
    defectsAreaLEdit = new QLineEdit("0.0");
    defectsAreaLEdit->setEnabled(false);
    totalAreaLEdit = new QLineEdit("0.0");    
    totalAreaLEdit->setEnabled(false);
    usefulAreaLEdit = new QLineEdit("0.0");
    usefulAreaLEdit->setEnabled(false);
    piecesLEdit = new QLineEdit("0");    
    piecesLEdit->setEnabled(false);
    usedLEdit = new QLineEdit("0.0");
    usedLEdit->setEnabled(false);
    
    resultsLay->addWidget(new QLabel("# Defects: "),0,0);
    resultsLay->addWidget(defectsLEdit,0,1);
    resultsLay->addWidget(new QLabel("Defects Area: "),0,2);
    resultsLay->addWidget(defectsAreaLEdit,0,3);
    resultsLay->addWidget(new QLabel("Placed Pieces: "),0,4);
    resultsLay->addWidget(piecesLEdit,0,5);
    resultsLay->addWidget(new QLabel("Total Area: "),1,0);
    resultsLay->addWidget(totalAreaLEdit,1,1);
    resultsLay->addWidget(new QLabel("Useful Area: "),1,2);
    resultsLay->addWidget(usefulAreaLEdit,1,3);
    resultsLay->addWidget(new QLabel("Used Area: "),1,4); 
    resultsLay->addWidget(usedLEdit,1,5);
    
    /* Sheet Group */
    sheetGro = new QGroupBox("Preview",centralWid);
    sheetLay = new QBoxLayout(QBoxLayout::LeftToRight,sheetGro);
    sheetGro->setLayout(sheetLay);
    
    sheetScr = new QScrollArea(sheetGro);
    sheetLab = new QLabel(sheetScr);
    
    sheetScr->setBackgroundRole(QPalette::Dark);
    sheetScr->setWidget(sheetLab);
    sheetScr->setAlignment(Qt::AlignCenter);
    
    sheetLab->setBackgroundRole(QPalette::Base);
    sheetLab->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    sheetLab->setScaledContents(true);
    sheetLay->addWidget(sheetScr);
    
    /* Start Button */
    startBut = new QPushButton();
    startBut->setIcon(QIcon("images/start.PNG"));
    startBut->setIconSize(QSize(100,100));
    startBut->setEnabled(false);
    
    /* Edit Button */
    editBut = new QPushButton();
    editBut->setIcon(QIcon("images/edit.PNG"));
    editBut->setIconSize(QSize(100,100));
    editBut->setEnabled(false);
    
    /* Global Settings */
    mainLay->addWidget(infoGro,0,0);
    mainLay->addWidget(parametersGro,0,1);
    mainLay->addWidget(resultsGro,0,2);
    mainLay->addWidget(startBut,0,3);
    mainLay->addWidget(editBut,0,4);
    mainLay->addWidget(sheetGro,1,0,2,5);

    setCentralWidget(centralWid);
    
}

/**
 * Crea las acciones. 
 */
void MainGUI::createActions(){
    openAct = new QAction(tr("&Open..."), this);
    openAct->setShortcut(tr("Ctrl+O"));
    connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
    
    saveAct = new QAction(tr("&Save..."), this);
    saveAct->setShortcut(tr("Ctrl+S"));
    connect(saveAct, SIGNAL(triggered()), this, SLOT(save()));

    zoomInAct = new QAction(tr("Zoom &In (25%)"), this);
    zoomInAct->setShortcut(tr("Ctrl++"));
    zoomInAct->setEnabled(false);
    connect(zoomInAct, SIGNAL(triggered()), this, SLOT(zoomIn()));

    zoomOutAct = new QAction(tr("Zoom &Out (25%)"), this);
    zoomOutAct->setShortcut(tr("Ctrl+-"));
    zoomOutAct->setEnabled(false);
    connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(zoomOut()));

    normalSizeAct = new QAction(tr("&Normal Size"), this);
    normalSizeAct->setShortcut(tr("Ctrl+."));
    normalSizeAct->setEnabled(false);
    connect(normalSizeAct, SIGNAL(triggered()), this, SLOT(normalSize()));

    fitToWindowAct = new QAction(tr("&Fit to Window"), this);
    fitToWindowAct->setEnabled(false);
    fitToWindowAct->setCheckable(true);
    fitToWindowAct->setShortcut(tr("Ctrl+F"));
    connect(fitToWindowAct, SIGNAL(triggered()), this, SLOT(fitToWindow()));
    
    vectorizeAct = new QAction(tr("&Vectorize..."), this);
    vectorizeAct->setShortcut(tr("Ctrl+V"));
    connect(vectorizeAct, SIGNAL(triggered()), this, SLOT(vectorize()));

    loadAct = new QAction(tr("&Load..."), this);
    loadAct->setShortcut(tr("Ctrl+L"));
    loadAct->setEnabled(false);
    connect(loadAct, SIGNAL(triggered()), this, SLOT(load()));
        
    connect(startBut,SIGNAL(clicked()),this,SLOT(start()));
    connect(editBut,SIGNAL(clicked()),this,SLOT(edit()));
}

/**
 * Actualiza las Acciones.
 */
void MainGUI::updateActions(){
    zoomInAct->setEnabled(!fitToWindowAct->isChecked());
    zoomOutAct->setEnabled(!fitToWindowAct->isChecked());
    normalSizeAct->setEnabled(!fitToWindowAct->isChecked());
}

/**
 * Crea los menús.
 */
void MainGUI::createMenus(){
    fileMenu = new QMenu(tr("&File"), this);
    fileMenu->addAction(openAct);
    fileMenu->addSeparator();
    fileMenu->addAction(saveAct);
    
    viewMenu = new QMenu(tr("&View"), this);
    viewMenu->addAction(zoomInAct);
    viewMenu->addAction(zoomOutAct);
    viewMenu->addAction(normalSizeAct);
    viewMenu->addSeparator();
    viewMenu->addAction(fitToWindowAct);
    
    flatsMenu = new QMenu(tr("F&lat"), this);
    flatsMenu->addAction(vectorizeAct);
    flatsMenu->addAction(loadAct);
    
    helpMenu = new QMenu(tr("&Help"), this);

    menuBar()->addMenu(fileMenu);
    menuBar()->addMenu(viewMenu);
    menuBar()->addMenu(flatsMenu);
    menuBar()->addMenu(helpMenu);
}

/**
 * Selecciona una imagen de disco duro.
 */
void MainGUI::open(){
    
    filename = QFileDialog::getOpenFileName(this,tr("Open File"), QDir::currentPath(),tr("Imagen JPEG (*.jpg *.jpeg *.jpe);;Todos los archivos (*.*)"));    
    
    if(!filename.isEmpty()){
        
        src = imread(filename.toStdString());                
        mask = Mat(src.size(),src.type(),cvScalar(255,255,255));
        fileinfo = QFileInfo(filename);
        
        sheetLab->setPixmap(MatToQPixmap(src));
        scaleFactor = 1.0;

        fitToWindowAct->setEnabled(true);
        updateActions();
        
        fitToWindowAct->setChecked(true);
        fitToWindow();

        if(!fitToWindowAct->isChecked())
             sheetLab->adjustSize(); 
        startBut->setEnabled(true);
    }
  
}

/**
 * Guarda una imagen en disco duro
 */
void MainGUI::save(){
    
    QString filename = QFileDialog::getSaveFileName(this,tr("Save Image"),QDir::currentPath(),tr("Imagen JPEG (*.jpg *.jpeg *.jpe)"));
    if(filename.contains(".JPG") || filename.contains(".jpg")){
        imwrite(filename.toStdString(),src);
    }
    else{
        imwrite(filename.toStdString() + ".JPG",src);
    }   
    
}

/**
 * Acerca la imagen
 */
void MainGUI::zoomIn(){
    
    scaleImage(1.25);
    
}

/**
 * Aleja la imagen.
 */
void MainGUI::zoomOut(){
    
    scaleImage(0.8);
    
}

/**
 * Muestra la imagen en su tamaÃ±o original.
 */
void MainGUI::normalSize(){
    
    sheetLab->adjustSize();
    scaleFactor = 1.0;
    
}

/**
 * Ajusta la barra de desplazamiento al tamaÃ±o de la imagen.
 * @param scrollBar Barra de desplazamiento a ajustar.
 * @param factor Valor de ajuste.
 */
void MainGUI::adjustScrollBar(QScrollBar * scrollBar, double factor){
    
    scrollBar->setValue(int(factor * scrollBar->value() + ((factor - 1) * scrollBar->pageStep() / 2)));
    
}

/**
 * Escala una imagen.
 * @param factor Valor de escala
 */
void MainGUI::scaleImage(double factor){
    
    scaleFactor *= factor;

    if(sheetLab->pixmap() != 0){
        sheetLab->resize(scaleFactor * sheetLab->pixmap()->size());
        adjustScrollBar(sheetScr->horizontalScrollBar(), factor);
        adjustScrollBar(sheetScr->verticalScrollBar(), factor);
    }
    zoomInAct->setEnabled(scaleFactor < 3.0);
    zoomOutAct->setEnabled(scaleFactor > 0.333);
    
}

/**
 * Ajusta el tamaÃ±o de la imagen al tamaÃ±o de la pantalla.
 */ 
void MainGUI::fitToWindow(){
    
    scaleFactor = 1.0;
    
    bool fitToWindow = fitToWindowAct->isChecked();
    scaleImage((double)sheetScr->size().height() / (double)src.rows);

    if (!fitToWindow) {
        normalSize();
    }
    updateActions();
    
}
/*
Magick::Image Mat2Magick(Mat & src){
   Magick::Image mgk(src.cols,src.rows,"BGR",MagickCore::CharPixel,(char *)src.data);
   return mgk;
}
*/
/**
 * Ejecuta procesos de Detección de Contorno y Detección de defectos.
 */
void MainGUI::start(){
    if(src.data){
        nameLEdit->setText(fileinfo.fileName());
        
        //cout << fileinfo.fileName().toStdString() << "\t";
        
        sizeLEdit->setText(QString::number(src.rows) + "x" + QString::number(src.cols));
        
        //Magick::Image image(filename.toStdString());
        Magick::Image image = Mat2Magick(src);
        Rect rect = findBarcode(src);
        string id = decodeBarcode(image,rect);
        idLEdit->setText(QString(id.c_str()));
        
        /** ADD IMAGE to DATABASE */
        //db::Execute("INSERT INTO \"PL_Imagen\" VALUES('" + id + "',lo_import('" + filename.toStdString() + "'),'now()')");
        /** END */
        
        Point cb_tl(rect.x,rect.y);
        Point cb_br(rect.x + rect.width,rect.y + rect.height);
        Point cb_tr(rect.x + rect.width,rect.y);
        Point cb_bl(rect.x,rect.y + rect.height);
        
        vector < Point > barcode;
        barcode.push_back(cb_tl);
        barcode.push_back(cb_tr);
        barcode.push_back(cb_br);
        barcode.push_back(cb_bl);
            
        Mat clone = src.clone();
        EdgeDefectsDetection(clone,contours,atoi(eKernelSizeLEdit->text().toStdString().c_str()),atoi(edThreshLEdit->text().toStdString().c_str()),atoi(edKernelSizeLEdit->text().toStdString().c_str()));
      
        drawContours(src,contours,0,cvScalar(0,255,0,0),2,8);
        
        drawContours(mask,contours,0,cvScalar(0,0,0),CV_FILLED);
        
        vector < Point > defects; 
        for(uint i = 1; i < contours.size(); i++){
            uint size = contours[i].size();
            for(uint j = 0; j < size; j++){
                defects.push_back(contours[i][j]);
            }
        }
        
        vector < Point > contour = contours[0];
               
        contours.clear();
        UnsupervisedClustering(defects,contours,70);
        
        contours.insert(contours.begin(),contour);
        
        vector < Rect > boundRect(contours.size());  
        
        for(uint j = 1; j < contours.size(); j++){
            boundRect[j] = boundingRect(Mat(contours[j]));
            
            
            Point da(boundRect[j].x,boundRect[j].y),
                  db(boundRect[j].x + boundRect[j].width,boundRect[j].y),
                  dc(boundRect[j].x,boundRect[j].y + boundRect[j].height),
                  dd(boundRect[j].x + boundRect[j].width,boundRect[j].y + boundRect[j].height),
                  dcenter(boundRect[j].x + boundRect[j].width / 2,boundRect[j].y + boundRect[j].height / 2);
            
            vector < Point > defects;
            defects.push_back(da); defects.push_back(db); defects.push_back(dd); defects.push_back(dc);
            
            if(!PointInContour(barcode,dcenter)){
                rectangle(src,boundRect[j],cvScalar(255,0,0),2,8);
                rectangle(mask,boundRect[j],cvScalar(255,255,255),CV_FILLED);
                rectangle(src,boundRect[j],cvScalar(255,0,0),2,8);
                rectangle(mask,boundRect[j],cvScalar(255,255,255),CV_FILLED);
                
                stringstream x; x << boundRect[j].x;
                stringstream y; y << boundRect[j].y;
                stringstream ancho; ancho << boundRect[j].width;
                stringstream largo; largo << boundRect[j].height;
            
                Execute("INSERT INTO \"PL_Defecto\"(id_tipo_defecto,id_imagen,x_pos,y_pos,ancho,largo,fecha_creacion) VALUES('1','" + idLEdit->text().toStdString() + "','" + x.str() + "','" + y.str() + "','" + ancho.str() + "','" + largo.str() + "','now()')");
                //cout << "INSERT INTO \"PL_Defecto\"(id_tipo_defecto,id_imagen,x_pos,y_pos,ancho,largo,fecha_creacion) VALUES('1','" + idLEdit->text().toStdString() + "','" + x.str() + "','" + y.str() + "','" + ancho.str() + "','" + largo.str() + "','now()')" << endl;
                
            }else{
                contours.erase(contours.begin() + j);
                j--;
            }           
        }
        
        // Calcular área de Defectos.
        float defectos = CalculateDefectsArea(contours);
        
        // Calcular área Total.
        float total = CalculateTotalArea(contours[0]);
        
        this->free = total - defectos;

        defectsLEdit->setText(QString::number(contours.size() - 1));
        defectsAreaLEdit->setText(QString::number(defectos,'f',2));
        totalAreaLEdit->setText(QString::number(total,'f',2));
        usefulAreaLEdit->setText(QString::number(free,'f',2));
        
        //cout << usefulAreaLEdit->text().toStdString() << endl;
        
        imwrite("mask.JPG",mask);
        
        QImage temporal;
        Mat rgb = src.clone();
        sheetLab->setPixmap(MatToQPixmap(rgb));
        
        loadAct->setEnabled(true);
        startBut->setEnabled(false);
        editBut->setEnabled(true);
    }
    else{
        QMessageBox::information(this, tr("Cutting Optimization"),
                                      tr("Seriously?. Come on man!. \nYou need to load an image first... \nDon't you think?"));
    }
}

void MainGUI::edit(){
    cout << "JIJIJIJ\n";
    QImage image = this->sheetLab->pixmap()->toImage();
    
    editWid = new EditGUI(image);
    editWid->setVisible(true);
    editWid->showMaximized();
}
